﻿using GrayParrot.Configuration;
using GrayParrot.Core;
using GrayParrot.ServiceBus.Configuration;
using Microsoft.ServiceBus.Messaging;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GrayParrot.ServiceBus
{
    /// <summary>
    /// Helper class for Microsoft Service Bus delayed messages...
    /// </summary>
    public static class DelayedMessagesHelper
    {
        /// <summary>
        /// Sends the delayed message.
        /// </summary>
        /// <param name="obj">The object.</param>
        /// <param name="message">The message.</param>
        /// <param name="delay">The delay.</param>
        /// <returns></returns>
        public static bool SendDelayedMessage(this Publisher obj, BrokeredMessage message)
        {
            bool retValue = false;

            try
            {
                if (obj != null)
                {
                    retValue = obj.Send(message);

                    if (retValue)
                    {
                        ReturnValue<bool> m_res = message.SaveDelayedMessageInfo();

                        if (m_res.HasValue && m_res.Value)
                        {
                            retValue = m_res.Value;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                GrayParrot.Logging.Logger.For<Publisher>().Error("Errore Publisher Delayed Message", ex);

                throw ex;
            }

            return retValue;
        }

        /// <summary>
        /// Saves the delayed message information...
        /// </summary>
        /// <param name="messageId">The message identifier.</param>
        /// <param name="delayDate">The delay date.</param>
        /// <returns></returns>
        public static ReturnValue<bool> SaveDelayedMessageInfo(Guid messageId, DateTime delayDate)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                ConfigurationManagerHelper pp = new ConfigurationManagerHelper();
                DelayedMessagesSettings m_dms = pp.GetDelayedMessagesSetting();

                if (m_dms != null)
                {
                    string m_sqlCommand = string.Format("INSERT INTO [psqf].[delayed_messages] ([MessageId], [CreationDate], [DelayDate], [Status]) VALUES (@param1, @param2, @param3, @param4)");

                    string cn = ConfigurationManager.ConnectionStrings[m_dms.ConnectionStringName].ConnectionString;

                    using (SqlConnection connection = new SqlConnection(cn))
                    {
                        connection.Open();

                        using (SqlCommand command = new SqlCommand(m_sqlCommand, connection))
                        {
                            SqlParameter p1 = new SqlParameter("param1", System.Data.SqlDbType.UniqueIdentifier);
                            p1.Value = messageId;
                            SqlParameter p2 = new SqlParameter("param2", System.Data.SqlDbType.DateTime);
                            p2.Value = DateTime.Now;
                            SqlParameter p3 = new SqlParameter("param3", System.Data.SqlDbType.DateTime);
                            p3.Value = delayDate;
                            SqlParameter p4 = new SqlParameter("param4", System.Data.SqlDbType.TinyInt);
                            p4.Value = (byte)DelayedMessageStatus.New;

                            command.Parameters.Add(p1);
                            command.Parameters.Add(p2);
                            command.Parameters.Add(p3);
                            command.Parameters.Add(p4);

                            command.ExecuteNonQuery();

                            retValue.HasValue = true;
                            retValue.Value = true;
                        }
                    }
                }
                else
                {
                    retValue.Message = "Missing Delayed Messages Settings...";
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public static ReturnValue<bool> SaveDelayedMessageInfo(this BrokeredMessage msg)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                ConfigurationManagerHelper pp = new ConfigurationManagerHelper();
                DelayedMessagesSettings m_dms = pp.GetDelayedMessagesSetting();

                if (m_dms != null)
                {
                    string m_sqlCommand = string.Format("INSERT INTO [psqf].[delayed_messages] ([MessageId], [CreationDate], [DelayDate], [Status]) VALUES (@param1, @param2, @param3, @param4)");

                    string cn = ConfigurationManager.ConnectionStrings[m_dms.ConnectionStringName].ConnectionString;

                    using (SqlConnection connection = new SqlConnection(cn))
                    {
                        connection.Open();

                        using (SqlCommand command = new SqlCommand(m_sqlCommand, connection))
                        {
                            SqlParameter p1 = new SqlParameter("param1", System.Data.SqlDbType.UniqueIdentifier);
                            p1.Value = Guid.Parse(msg.MessageId);
                            SqlParameter p2 = new SqlParameter("param2", System.Data.SqlDbType.DateTime);
                            p2.Value = DateTime.Now;
                            SqlParameter p3 = new SqlParameter("param3", System.Data.SqlDbType.DateTime);
                            p3.Value = msg.ScheduledEnqueueTimeUtc;
                            SqlParameter p4 = new SqlParameter("param4", System.Data.SqlDbType.TinyInt);
                            p4.Value = (byte)DelayedMessageStatus.New;

                            command.Parameters.Add(p1);
                            command.Parameters.Add(p2);
                            command.Parameters.Add(p3);
                            command.Parameters.Add(p4);

                            command.ExecuteNonQuery();

                            retValue.HasValue = true;
                            retValue.Value = true;
                        }
                    }
                }
                else
                {
                    retValue.Message = "Missing Delayed Messages Settings...";
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        /// <summary>
        /// Determines whether this Brokered message is a delayed one...
        /// </summary>
        /// <param name="msg">The MSG.</param>
        /// <returns></returns>
        public static ReturnValue<bool> CheckDelayed(this BrokeredMessage msg)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                retValue = CheckDelayedMessage(msg.MessageId);
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        /// <summary>
        /// Gets the delayed message status...
        /// </summary>
        /// <param name="msg">The MSG.</param>
        /// <returns></returns>
        public static ReturnValue<DelayedMessageStatus> GetStatus(this BrokeredMessage msg)
        {
            ReturnValue<DelayedMessageStatus> retValue = new ReturnValue<DelayedMessageStatus>(DelayedMessageStatus.None);

            try
            {
                retValue = CheckStatus(msg.MessageId);
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        /// <summary>
        /// Gets the delayed message status...
        /// </summary>
        /// <param name="messageId">The message identifier.</param>
        /// <returns></returns>
        public static ReturnValue<DelayedMessageStatus> GetStatus(Guid messageId)
        {
            ReturnValue<DelayedMessageStatus> retValue = new ReturnValue<DelayedMessageStatus>(DelayedMessageStatus.None);

            try
            {
                retValue = CheckStatus(messageId);
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        /// <summary>
        /// Sets the status of a delayed message...
        /// </summary>
        /// <param name="messageId">The message identifier.</param>
        /// <param name="status">The status.</param>
        /// <returns></returns>
        public static ReturnValue<bool> SetStatus(Guid messageId, DelayedMessageStatus status)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            ConfigurationManagerHelper pp = new ConfigurationManagerHelper();
            DelayedMessagesSettings m_dms = pp.GetDelayedMessagesSetting();

            if (m_dms != null)
            {
                int m_status = (int)status;

                string m_updSqlCommand = string.Format("UPDATE {0}.{1} SET Status = {2} WHERE MessageId='{3}'", m_dms.SchemaName, m_dms.TableName, m_status, messageId);

                string cn = ConfigurationManager.ConnectionStrings[m_dms.ConnectionStringName].ConnectionString;

                using (SqlConnection connection = new SqlConnection(cn))
                {
                    connection.Open();

                    using (SqlCommand command = new SqlCommand(m_updSqlCommand, connection))
                    {
                        command.ExecuteNonQuery();

                        retValue.HasValue = true;
                        retValue.Value = true;
                    }
                }
            }
            else
            {
                retValue.Message = "Missing Delayed Messages Settings...";
            }

            return retValue;
        }

        /// <summary>
        /// Checks the delayed message status...
        /// </summary>
        /// <param name="messageId">The message identifier.</param>
        /// <returns></returns>
        internal static ReturnValue<DelayedMessageStatus> CheckStatus(Guid messageId)
        {
            ReturnValue<DelayedMessageStatus> retValue = new ReturnValue<DelayedMessageStatus>(DelayedMessageStatus.None);

            ConfigurationManagerHelper pp = new ConfigurationManagerHelper();
            DelayedMessagesSettings m_dms = pp.GetDelayedMessagesSetting();

            if (m_dms != null)
            {
                string m_sqlCommand = string.Format("SELECT Status FROM {0}.{1} WHERE MessageId='{2}'", m_dms.SchemaName, m_dms.TableName, messageId);

                string cn = ConfigurationManager.ConnectionStrings[m_dms.ConnectionStringName].ConnectionString;

                using (SqlConnection connection = new SqlConnection(cn))
                {
                    connection.Open();

                    // default state is NONE (undefined)... 
                    DelayedMessageStatus m_status = DelayedMessageStatus.None;
                    retValue.HasValue = true;
                    retValue.Value = m_status;
                    using (SqlCommand command = new SqlCommand(m_sqlCommand, connection))
                    {
                        using (SqlDataReader reader = command.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                byte m_value = reader.GetByte(0);

                                m_status = (DelayedMessageStatus)m_value;

                                retValue.HasValue = true;
                                retValue.Value = m_status;
                            }
                        }
                    }
                }
            }
            else
            {
                retValue.Message = "Missing Delayed Messages Settings...";
            }

            return retValue;
        }

        /// <summary>
        /// Checks the delayed message status...
        /// </summary>
        /// <param name="messageId">The message identifier.</param>
        /// <returns></returns>
        internal static ReturnValue<DelayedMessageStatus> CheckStatus(string messageId)
        {
            ReturnValue<DelayedMessageStatus> retValue = new ReturnValue<DelayedMessageStatus>(DelayedMessageStatus.None);

            ConfigurationManagerHelper pp = new ConfigurationManagerHelper();
            DelayedMessagesSettings m_dms = pp.GetDelayedMessagesSetting();

            if (m_dms != null)
            {
                string m_sqlCommand = string.Format("SELECT Status FROM {0}.{1} WHERE MessageId='{2}'", m_dms.SchemaName, m_dms.TableName, Guid.Parse(messageId));

                string cn = ConfigurationManager.ConnectionStrings[m_dms.ConnectionStringName].ConnectionString;

                using (SqlConnection connection = new SqlConnection(cn))
                {
                    connection.Open();

                    // default state is NONE (undefined)... 
                    DelayedMessageStatus m_status = DelayedMessageStatus.None;
                    retValue.HasValue = true;
                    retValue.Value = m_status;
                    using (SqlCommand command = new SqlCommand(m_sqlCommand, connection))
                    {
                        using (SqlDataReader reader = command.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                byte m_value = reader.GetByte(0);

                                m_status = (DelayedMessageStatus)m_value;

                                retValue.HasValue = true;
                                retValue.Value = m_status;
                            }
                        }
                    }
                }
            }
            else
            {
                retValue.Message = "Missing Delayed Messages Settings...";
            }

            return retValue;
        }

        /// <summary>
        /// Gets the delayed message status...
        /// </summary>
        /// <param name="messageId">The message identifier.</param>
        /// <returns></returns>
        internal static ReturnValue<bool> CheckDelayedMessage(string messageId)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            ConfigurationManagerHelper pp = new ConfigurationManagerHelper();
            DelayedMessagesSettings m_dms = pp.GetDelayedMessagesSetting();

            if (m_dms != null)
            {
                string m_sqlCommand = string.Format("SELECT Count(*) FROM {0}.{1} WHERE MessageId='{2}'", m_dms.SchemaName, m_dms.TableName, Guid.Parse(messageId));

                string cn = ConfigurationManager.ConnectionStrings[m_dms.ConnectionStringName].ConnectionString;

                using (SqlConnection connection = new SqlConnection(cn))
                {
                    connection.Open();

                    using (SqlCommand command = new SqlCommand(m_sqlCommand, connection))
                    {
                        int m_numberOfRows = command.ExecuteNonQuery();

                        retValue.HasValue = true;
                        retValue.Value = (m_numberOfRows == 0 ? false : true);
                    }
                }
            }
            else
            {
                retValue.Message = "Sezione Delayed message mancante...";
            }

            return retValue;
        }

        /// <summary>
        /// Cancels the delayed message...
        /// </summary>
        /// <param name="messageId">The message identifier.</param>
        /// <returns></returns>
        internal static ReturnValue<bool> CancelDelayedMessage(string messageId)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            ConfigurationManagerHelper pp = new ConfigurationManagerHelper();
            DelayedMessagesSettings m_dms = pp.GetDelayedMessagesSetting();

            if (m_dms != null)
            {
                string m_updSqlCommand = string.Format("UPDATE {0}.{1} SET Status = 4 WHERE MessageId='{2}'", m_dms.SchemaName, m_dms.TableName, Guid.Parse(messageId));

                string cn = ConfigurationManager.ConnectionStrings[m_dms.ConnectionStringName].ConnectionString;

                using (SqlConnection connection = new SqlConnection(cn))
                {
                    connection.Open();

                    using (SqlCommand command = new SqlCommand(m_updSqlCommand, connection))
                    {
                        command.ExecuteNonQuery();

                        retValue.HasValue = true;
                        retValue.Value = true;
                    }
                }
            }
            else
            {
                retValue.Message = "Missing Delayed Messages Settings...";
            }

            return retValue;
        }

        /// <summary>
        /// Set the delayed message as Elaborated...
        /// </summary>
        /// <param name="messageId">The message identifier.</param>
        /// <returns></returns>
        internal static ReturnValue<bool> ElaboratedDelayedMessage(string messageId)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                ConfigurationManagerHelper pp = new ConfigurationManagerHelper();
                DelayedMessagesSettings m_dms = pp.GetDelayedMessagesSetting();

                if (m_dms != null)
                {
                    string m_updSqlCommand = string.Format("UPDATE {0}.{1} SET Status = 3 WHERE MessageId='{2}'", m_dms.SchemaName, m_dms.TableName, Guid.Parse(messageId));

                    string cn = ConfigurationManager.ConnectionStrings[m_dms.ConnectionStringName].ConnectionString;

                    using (SqlConnection connection = new SqlConnection(cn))
                    {
                        connection.Open();

                        using (SqlCommand command = new SqlCommand(m_updSqlCommand, connection))
                        {
                            command.ExecuteNonQuery();

                            retValue.HasValue = true;
                            retValue.Value = true;

                        }
                    }
                }
                else
                {
                    retValue.Message = "Missing Delayed Messages Settings...";
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;                
            }

            return retValue;
        }

        /// <summary>
        /// Set the delayed message in elaboration status...
        /// </summary>
        /// <param name="messageId">The message identifier.</param>
        /// <returns></returns>
        internal static ReturnValue<bool> ElaboratesDelayedMessage(string messageId)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                ConfigurationManagerHelper pp = new ConfigurationManagerHelper();
                DelayedMessagesSettings m_dms = pp.GetDelayedMessagesSetting();

                if (m_dms != null)
                {
                    string m_updSqlCommand = string.Format("UPDATE {0}.{1} SET Status = 3 WHERE MessageId='{2}'", m_dms.SchemaName, m_dms.TableName, Guid.Parse(messageId));

                    string cn = ConfigurationManager.ConnectionStrings[m_dms.ConnectionStringName].ConnectionString;

                    using (SqlConnection connection = new SqlConnection(cn))
                    {
                        connection.Open();

                        using (SqlCommand command = new SqlCommand(m_updSqlCommand, connection))
                        {
                            command.ExecuteNonQuery();

                            retValue.HasValue = true;
                            retValue.Value = true;

                        }
                    }
                }
                else
                {
                    retValue.Message = "Missing Delayed Messages Settings...";
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }
    }
}
